package com.example.dictionary.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 下载路径映射服务
 * 用于管理资源和实际下载路径的映射关系
 * 映射数据存储在 data/dict/datadict.json 文件中
 */
@Service
public class DownloadPathMappingService {
    
    private static final Logger log = LoggerFactory.getLogger(DownloadPathMappingService.class);
    
    private static final String MAPPING_FILE_PATH = "data/dict/datadict.json";
    
    private final ObjectMapper objectMapper;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    
    /**
     * 映射数据结构：
     * Key: 资源标识符（如 GitHub URL + branch，或词典源名称 + URL）
     * Value: 映射信息（包含下载路径和下载时间）
     */
    private Map<String, PathMappingEntry> mappings = new HashMap<>();
    
    public DownloadPathMappingService() {
        this.objectMapper = new ObjectMapper();
        // 注册 JavaTimeModule 以支持 LocalDateTime 等 Java 8 时间类型的序列化
        this.objectMapper.registerModule(new JavaTimeModule());
        // 禁用将日期写为时间戳，使用 ISO-8601 字符串格式
        this.objectMapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
    
    /**
     * 应用启动时加载映射文件
     */
    @PostConstruct
    public void loadMappings() {
        Path mappingFile = Paths.get(MAPPING_FILE_PATH);
        
        // 确保目录存在
        try {
            if (mappingFile.getParent() != null) {
                Files.createDirectories(mappingFile.getParent());
            }
        } catch (IOException e) {
            log.error("创建映射文件目录失败: {}", mappingFile.getParent(), e);
            return;
        }
        
        // 如果文件不存在，创建空映射
        if (!Files.exists(mappingFile)) {
            log.info("映射文件不存在，将创建新文件: {}", mappingFile);
            saveMappings();
            return;
        }
        
        // 读取并加载映射
        lock.writeLock().lock();
        try {
            String json = Files.readString(mappingFile);
            if (json == null || json.trim().isEmpty()) {
                log.info("映射文件为空，使用空映射");
                mappings = new HashMap<>();
            } else {
                TypeFactory typeFactory = objectMapper.getTypeFactory();
                MapType mapType = typeFactory.constructMapType(
                    Map.class, 
                    String.class, 
                    PathMappingEntry.class
                );
                Map<String, PathMappingEntry> loadedMappings = objectMapper.readValue(json, mapType);
                mappings = loadedMappings != null ? loadedMappings : new HashMap<>();
                log.info("成功加载 {} 条下载路径映射", mappings.size());
            }
        } catch (IOException e) {
            log.error("加载映射文件失败: {}", mappingFile, e);
            mappings = new HashMap<>();
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    /**
     * 保存映射到文件
     */
    private void saveMappings() {
        Path mappingFile = Paths.get(MAPPING_FILE_PATH);
        
        lock.readLock().lock();
        try {
            String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(mappings);
            Files.writeString(mappingFile, json);
            log.info("保存 {} 条映射到文件: {}", mappings.size(), mappingFile);
        } catch (IOException e) {
            log.error("保存映射文件失败: {}", mappingFile, e);
        } finally {
            lock.readLock().unlock();
        }
    }
    
    /**
     * 获取资源的下载路径
     * 
     * @param resourceKey 资源标识符（如 GitHub URL + branch，或词典源名称 + URL）
     * @return 下载路径（如果存在）
     */
    public Optional<String> getDownloadPath(String resourceKey) {
        lock.readLock().lock();
        try {
            PathMappingEntry entry = mappings.get(resourceKey);
            if (entry != null && entry.getDownloadPath() != null) {
                // 检查路径是否仍然存在
                Path path = Paths.get(entry.getDownloadPath());
                if (Files.exists(path)) {
                    return Optional.of(entry.getDownloadPath());
                } else {
                    log.warn("映射的路径不存在，已删除映射: {} -> {}", resourceKey, entry.getDownloadPath());
                    // 路径不存在，删除映射
                    lock.readLock().unlock();
                    lock.writeLock().lock();
                    try {
                        mappings.remove(resourceKey);
                        saveMappings();
                    } finally {
                        lock.writeLock().unlock();
                        lock.readLock().lock();
                    }
                }
            }
            return Optional.empty();
        } finally {
            lock.readLock().unlock();
        }
    }
    
    /**
     * 保存资源的下载路径映射
     * 
     * @param resourceKey 资源标识符
     * @param downloadPath 实际下载路径
     */
    public void saveMapping(String resourceKey, String downloadPath) {
        if (resourceKey == null || resourceKey.trim().isEmpty()) {
            log.warn("资源标识符为空，跳过保存映射");
            return;
        }
        if (downloadPath == null || downloadPath.trim().isEmpty()) {
            log.warn("下载路径为空，跳过保存映射");
            return;
        }
        
        lock.writeLock().lock();
        try {
            PathMappingEntry entry = new PathMappingEntry();
            entry.setDownloadPath(downloadPath.trim());
            entry.setDownloadTime(LocalDateTime.now());
            
            String trimmedKey = resourceKey.trim();
            String trimmedPath = downloadPath.trim();
            
            // 检查是否是更新已存在的映射
            boolean isUpdate = mappings.containsKey(trimmedKey);
            mappings.put(trimmedKey, entry);
            saveMappings();
            
            if (isUpdate) {
                log.info("更新下载路径映射: {} -> {}", trimmedKey, trimmedPath);
            } else {
                log.info("新增下载路径映射: {} -> {} (总计: {} 条)", trimmedKey, trimmedPath, mappings.size());
            }
        } catch (Exception e) {
            log.error("保存映射时发生异常: resourceKey={}, downloadPath={}", resourceKey, downloadPath, e);
            throw e;
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    /**
     * 删除映射
     * 
     * @param resourceKey 资源标识符
     */
    public void removeMapping(String resourceKey) {
        if (resourceKey == null || resourceKey.trim().isEmpty()) {
            return;
        }
        
        lock.writeLock().lock();
        try {
            mappings.remove(resourceKey.trim());
            saveMappings();
            log.info("删除下载路径映射: {}", resourceKey);
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    /**
     * 获取所有映射
     * 
     * @return 所有映射的副本
     */
    public Map<String, PathMappingEntry> getAllMappings() {
        lock.readLock().lock();
        try {
            return new HashMap<>(mappings);
        } finally {
            lock.readLock().unlock();
        }
    }
    
    /**
     * 构建 GitHub 仓库的资源标识符
     * 
     * @param gitUrl GitHub URL
     * @param branch 分支名称
     * @return 资源标识符
     */
    public String buildGitHubResourceKey(String gitUrl, String branch) {
        String normalizedUrl = gitUrl != null ? gitUrl.trim() : "";
        String normalizedBranch = branch != null ? branch.trim() : "master";
        return String.format("github:%s:%s", normalizedUrl, normalizedBranch);
    }
    
    /**
     * 构建词典源资源的资源标识符
     * 
     * @param sourceName 词典源名称
     * @param downloadUrl 下载 URL
     * @return 资源标识符
     */
    public String buildDictionarySourceResourceKey(String sourceName, String downloadUrl) {
        String normalizedName = sourceName != null ? sourceName.trim() : "";
        String normalizedUrl = downloadUrl != null ? downloadUrl.trim() : "";
        return String.format("dict:%s:%s", normalizedName, normalizedUrl);
    }
    
    /**
     * 路径映射条目
     */
    public static class PathMappingEntry {
        private String downloadPath;
        private LocalDateTime downloadTime;
        
        public String getDownloadPath() {
            return downloadPath;
        }
        
        public void setDownloadPath(String downloadPath) {
            this.downloadPath = downloadPath;
        }
        
        public LocalDateTime getDownloadTime() {
            return downloadTime;
        }
        
        public void setDownloadTime(LocalDateTime downloadTime) {
            this.downloadTime = downloadTime;
        }
    }
}

